package com.jcake.vimlike.internal;

import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IRegion;

public class TextUtils {

	public static int findFirstWordPosition(String line, int offset) {
		// cases:
		// 1. begining of string
		// 2. end of string
		// 3. middle of the word
		// 4. between two words

		// case 1.
		if (offset == 0) {
			return findFirstWordAfter(line, 0);
		}

		// case 2.
		if (offset == line.length()) {
			return findFirstWordBefore(line, line.length() - 1);
		}

		char charBefore = line.charAt(offset - 1);
		char charAfter = line.charAt(offset);
		if (!Character.isWhitespace(charBefore) && !Character.isWhitespace(charAfter)) {
			// case 3.
			return findFirstWordBefore(line, offset);
		}
		if (Character.isWhitespace(charAfter) || Character.isWhitespace(charBefore)) {
			// case 4.
			int idx = findFirstWordAfter(line, offset);
			if (idx == -1) {
				return findFirstWordBefore(line, offset);
			} else {
				return idx;
			}
		}
		return -1;
	}

	public static int findFirstWordBefore(String line, int before) {
		// skip whitespaces
		int idx = -1;
		for (int i = before; i >= 0; i--) {
			if (!Character.isWhitespace(line.charAt(i))) {
				idx = i;
				break;
			}
		}
		if (idx == -1) {
			return -1;
		}

		char c = line.charAt(idx);
		if (Character.isLetterOrDigit(c)) {
			for (int i = idx; i >= 0; i--) {
				if (!Character.isLetterOrDigit(line.charAt(i))) {
					return i + 1;
				}
			}
		} else { // non letter digit-word
			for (int i = idx; i >= 0; i--) {
				if (Character.isLetterOrDigit(line.charAt(i)) || Character.isWhitespace(line.charAt(i))) {
					return i + 1;
				}
			}
		}
		return 0; 
	}

	public static int findFirstWordAfter(String line, int from) {
		for (int i = from; i < line.length(); i++) {
			if (!Character.isWhitespace(line.charAt(i))) {
				return i;
			}
		}
		return -1;
	}

	public static int findNthWordEnd(String restOfTheText, int from, Integer numberOfWords) {
		if (from < 0) {
			return -1;
		}
		int idx = from;
		for (int i = 0; i < numberOfWords; i++) {
			idx = findWordEnd(restOfTheText, idx);
			if (i != numberOfWords - 1) {
				if (idx < restOfTheText.length() - 1) {
					idx++;
				} else {
					return restOfTheText.length() - 1;
				}
			}
		}
		return idx;
	}

	public static int findWordEnd(String restOfTheText, int from) {
		//skip whitespaces
		int idx = from; 
		for (int i = idx; i < restOfTheText.length(); i++) {
			if (!Character.isWhitespace(restOfTheText.charAt(i))) {
				idx = i;
				break;
			}
		}

		if (Character.isLetterOrDigit(restOfTheText.charAt(idx))) {
			for (int i = idx; i < restOfTheText.length(); i++) {
				if (Character.isLetterOrDigit(restOfTheText.charAt(i))) {
					idx = i;
				} else {
					break;
				}
			}
		} else {
			for (int i = idx; i < restOfTheText.length(); i++) {
				if (!Character.isLetterOrDigit(restOfTheText.charAt(i))) {
					idx = i;
				} else {
					break;
				}
			}
		}
		return idx;
	}

	public static boolean isBlank(String s) {
		for (int i = 0; i < s.length(); i++) {
			if (!Character.isWhitespace(s.charAt(i))) {
				return false;
			}
		}
		return true;
	}

	public static String removeLeadingWhitespaces(String text) {
		StringBuilder buff = new StringBuilder(text);
		while (buff.length() > 0 && Character.isWhitespace(buff.charAt(0))) {
			buff.deleteCharAt(0);
		}
		return buff.toString();
	}

	public static int findSentenceStartBefore(String text, int offset) {
		// sentence start may be:
		// - blank line,
		// - nonwhitespace after .|!|?  followed by optional )|]|"|' followed by whitespace
		// - BOF
		if (offset >= text.length()) {
			offset = text.length() - 1;
		}

		StringBuilder maybeStart = new StringBuilder();
		for (int i = offset; i >= 0; i--) {
			if (i == 0) {
				// BOF
				return findSentenceStartAfter(text, 0);
			}
			char c = text.charAt(i);
			switch (c) {
				case ' ':
				case '\t':
				case '\n':
				case '\r':
				case ')':
				case ']':
				case '\'':
				case '"':
				case '.':
				case '!':
				case '?':
					maybeStart.insert(0, c);
					break;
				default:
					maybeStart.setLength(0);
			}
			// if maybeStart is a sentence end then return the right position
			if (maybeStart.toString().matches("^(\\.|!|\\?)(\\)|\\]|'|\")*?\\s+")) {
				int position = i + maybeStart.length();
				// consume whitespaces
				for (int j = position; j < text.length(); j++) {
					if (Character.isWhitespace(text.charAt(position))) {
						position++;
					} else {
						break;
					}
				}
				if (position >= text.length()) {
					// EOF
					return -1;
				}
				return position;
			}
		}
		return -1;
	}

	private static int findSentenceStartAfter(String text, int start) {
		for (int i = start; i < text.length(); i++) {
			if (!Character.isWhitespace(text.charAt(i))) {
				return i;
			}
		}
		return -1;
	}

	public static int findNSentencesEnd(String text, int start, int count) {
		int position = start;
		for (int i = 0; i < count; i++) {
			position = findSentenceEnd(text, position);
			if (position == -1) {
				return -1;
			}
			if (position == text.length() - 1) {
				return position;
			}
			if (i < count - 1) {
				position++;
			}
		}
		return position;
	}

	public static int findSentenceEnd(String text, int start) {
		StringBuilder maybeEnd = new StringBuilder();
		for (int i = start; i < text.length(); i++) {
			char c = text.charAt(i);
			switch (c) {
				case ' ':
				case '\t':
				case '\n':
				case '\r':
				case ')':
				case ']':
				case '\'':
				case '"':
				case '.':
				case '!':
				case '?':
					maybeEnd.append(c);
					break;
				default:
					maybeEnd.setLength(0);
			}
			if (maybeEnd.toString().matches("^(\\.|!|\\?)(\\)|\\]|'|\")*?\\s+")) {
				int position = i - 1;
				return position;
			}
			if (i == text.length() - 1) {
				return text.length() - 1;
			}
		}

		return -1;
	}
	
	public static String removeLeadingBlankLines(String text) {
		return text.replaceAll("\\\n\\s*?\\\n", "");
	}

	public static int findParagraphStartBefore(String text, int offset) {
		// paragraph start may be:
		// 1. BOF
		// 2. line begining if previous line was blank
		// 3. if we are at empty line then we are looking for the next paragraph
		Document document = new Document(text);
		if (offset < 0 || offset >= document.getLength()) {
			return -1;
		}
		try {
			int lineNumber = document.getLineOfOffset(offset);
			IRegion lineInfo = document.getLineInformationOfOffset(offset);
			String line = document.get(lineInfo.getOffset(), lineInfo.getLength());
			if (isBlank(line)) {
				// looking for next paragraph
				return findParagraphStartAfter(text, offset);
			}

			// line is not blank, so we have to find BOF or blank line before it
			if (lineNumber == 0) {
				// BOF
				return 0;
			}
			for (int i = lineNumber - 1; i >= 0; i--) {
				lineInfo = document.getLineInformation(i);
				line = document.get(lineInfo.getOffset(), lineInfo.getLength());
				if (isBlank(line)) {
					// line before is blank - returning next line offset
					return document.getLineInformation(i + 1).getOffset(); 
				}
				if (i == 0) {
					return 0;
				}
			}
		} catch (BadLocationException e) {
			
		}
		return -1;
	}

	private static int findParagraphStartAfter(String text, int offset) {
		Document document = new Document(text);
		try {
			int startLine = document.getLineOfOffset(offset);
			for (int i = startLine; i < document.getNumberOfLines(); i++) {
				IRegion lineInfo = document.getLineInformation(i);
				String line = document.get(lineInfo.getOffset(), lineInfo.getLength());
				if (!isBlank(line)) {
					return lineInfo.getOffset();
				}
			}
			
		} catch (BadLocationException e) {
		}
		return -1;
	}

	/**
	 * Tries to find <code>count</code> paragraphs in 
	 * @param document
	 * @param paraStart
	 * @param count
	 * @return
	 */
	public static int findNParagraphEndAfter(String text, int paraStart, Integer count) {
		int start = paraStart;
		for (int i = 0; i < count; i++) {
			int end = findParagraphEndAfter(text, start);
			if (i == count - 1) {
				return end;
			}
			Document document = new Document(text);
			try {
				int currentLine = document.getLineOfOffset(end);
				int numberOfLines = document.getNumberOfLines();
				if (currentLine < numberOfLines - 1) {
					end = document.getLineInformation(currentLine + 1).getOffset(); 
				}
			} catch (BadLocationException e) {
			}
			start = findParagraphStartAfter(text, end);
		}
		return -1;
	}

	private static int findParagraphEndAfter(String text, int start) {
		Document document = new Document(text);
		int numberOfLines = document.getNumberOfLines();
		try {
			int lineNumber = document.getLineOfOffset(start);
			for (int i = lineNumber; i < numberOfLines; i++) {
				IRegion lineInfo = document.getLineInformation(i);
				String line = document.get(lineInfo.getOffset(), lineInfo.getLength());
				if (isBlank(line)) {
					return lineInfo.getOffset();
				}
				if (lineNumber == numberOfLines - 1) {
					return document.getLength() - 1;
				}
			}
		} catch (BadLocationException e) {
		}
		return -1;
	}

}
